#include "calc.h"

extern "C" BaseModule* create(string str)
{
	return new Calc(str);
}

extern "C" void destroy(BaseModule* mod)
{
	delete mod;
}

extern "C" BaseModule* ccreate()
{
	return new Calc(string("0"));
}

Calc::Calc(string str)
{
	cin.putback('\n');
	for (int i = str.length() - 1; i >= 0; i--)
    	cin.putback(str[i]);

    no_of_errors = 0;
    curr_tok = PRINT;
    error_code = "\0";

    table["pi"] = 3.1415926535897932385;
    table["e"] = 2.7182818284590452354;

    get_token();
    answer = expr(false);
        
}

string Calc::showAnswer()
{
	if (error_code == "\0"){
    	stringstream ss (stringstream::in | stringstream::out);
        ss << answer;
        string str;
       	ss >> str;

		return str;
	} else
		return error_code;
}

double Calc::error(const char* s)
{
    ++no_of_errors;
    error_code = s;
    return 1;
}

Token_value Calc::get_token()
{
    char ch;

    do
	{
    	if(!cin.get(ch))
        	return curr_tok = END;
    } while (ch != '\n' && isspace(ch));

	switch (ch)
	{
        case ';': case '\n':
        	return curr_tok = PRINT;

        case '*': case '/': case '+': case '-':
        case '(': case ')': case '=':
            return curr_tok = Token_value(ch);

        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
        case '.':
            cin.putback(ch);
            cin >> number_value;
            return curr_tok = NUMBER;

        default:
        	if (isalpha(ch))
			{
            	string_value = ch;
                
				while (cin.get(ch) && isalnum(ch))
                	string_value += ch;
                    cin.putback(ch);
                    return curr_tok=NAME;
        	 }
             error("bad token");
             return curr_tok = PRINT;
	}
}


double Calc::prim(bool get)
{
	if (get)
		get_token();

    switch (curr_tok)
	{
    	case NUMBER:
       	{	double v = number_value;
            get_token();
            return v;
        }
        case NAME:
        {   double& v = table[string_value];
            if (get_token() == ASSIGN)
				v = expr(true);
            return v;
        }
        case MINUS:
            return -prim(true);
        case LP:
        {   double e = expr(true);
            if (curr_tok != RP)
            	return error(") expected");
            get_token();
                return e;
        }
        default:
                return error("primary expected");
	}
}

double Calc::term(bool get)
{
	double left = prim(get);

    for (;;)
    	switch (curr_tok)
		{
        	case MUL:
            	left *= prim(true);
                break;
            case DIV:
                if (double d = prim(true))
				{
                	left /= d;
                    break;
               	}
                return error("бесконечность!");
            default:
                return left;
		}
}

double Calc::expr(bool get)        
{
	double left = term(get);

	for (;;)                           
		switch (curr_tok)
		{
        	case PLUS:
            	left += term(true);
                break;
           	case MINUS:
                left -= term(true);
                break;
           	default:
                return left;
		}
}

